package com.xunk.equipment.service.acrel.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.xunk.common.core.constant.CacheConstants;
import com.xunk.common.core.utils.DateUtils;
import com.xunk.common.core.utils.StringUtils;
import com.xunk.common.redis.service.RedisService;
import com.xunk.equipment.domain.EquipmentDO;
import com.xunk.equipment.influxdb.InfluxCustomMapper;
import com.xunk.equipment.influxdb.entity.WaterFlowmeterEntity;
import com.xunk.equipment.mapper.EquipmentMapper;
import com.xunk.equipment.service.acrel.constant.FlowmeterUnitEnum;
import com.xunk.equipment.service.acrel.service.AcrelSmartService;
import com.xunk.equipment.service.acrel.service.PublishMessageComponent;
import com.xunk.equipment.service.acrel.vo.AcrelFlowmeterDetailVO;
import com.xunk.equipment.service.acrel.vo.AcrelWaterFlowmeterVO;
import com.xunk.equipment.service.acrel.vo.SmartWaterVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import com.xunk.common.core.domain.EquipmentResponse;

/**
 * @ProjectName: xunk
 * @Package: com.xunk.equipment.service.acrel.service.impl
 * @ClassName: SmartWaterServiceImpl
 * @Author: dongxiaoxin
 * @Description: 安科瑞水表服务
 * @Date: 2021/3/30 9:29
 * @Version: 1.0
 */
@Service("acrel-water-meter")
@Slf4j
public class SmartWaterServiceImpl extends PublishMessageComponent implements AcrelSmartService {

    @Autowired
    private RedisService service;

    @Autowired
    InfluxCustomMapper mapper;

    @Autowired
    EquipmentMapper equipmentMapper;

    private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

    @Override
    @SuppressWarnings("unchecked")
    public <T> List<T> queryFlowmeter(Long homeId, String unit) throws ParseException {

        if (FlowmeterUnitEnum.getDefault().equals(unit)) {
            return (List<T>)AutoDayData();
        }else if ("month".equals(unit)){
            return (List<T>)AutoData();
        }else if ("season".equals(unit)){
            return (List<T>)AutoSeasonData();
        }else if ("halfYear".equals(unit)){
            return (List<T>)AutoHalfYear();
        }else {
            return (List<T>)AutoYear();
        }
//        if (homeId != 8){
//
//        }else {
//            String querySql = String.format("SELECT * FROM \"water_tb\" WHERE time > now() - %s and home_id = '%s'",
//                    FlowmeterUnitEnum.parseDeviceEnum(unit).getTime(), homeId);
//            List<WaterFlowmeterEntity> list = mapper.query(new Query(querySql, "paramter_db"), WaterFlowmeterEntity.class);
//            int day = Integer.parseInt(DateUtils.parseDateToStr("dd",new Date()));
//            log.info("今天NOW" + LocalDateTime.now().toString() + "趋势图数据详细信息 —> 时间单位：" + unit + "，SQl：" + querySql + ",查询结果："+ list.size());
//            List<SmartWaterVO> voList = new ArrayList<>();
//            if (CollectionUtils.isEmpty(list)){
//                return new ArrayList<>();
//            }
//            ListIterator<WaterFlowmeterEntity> iterator = list.listIterator();
//            //  得到第一条数据是周几 -> 然后当查询每季度用水的时候 就按这个值判断一周的循环
//            String weekDay = DateUtils.getWeekDayString(df.parse(list.get(0).getTime().toString()));
//            while (iterator.hasNext()){
//
//                WaterFlowmeterEntity entity = iterator.next();
//                Date parse;
//                try {
//                    parse = df.parse(entity.getTime().toString());
//                }catch (Exception e){
//                    log.error("时间格式化失败：{}",entity.getTime());
//                    continue;
//                }
//                if (FlowmeterUnitEnum.getDefault().equals(unit)){
//                    //  最近一天  时间单位是小时
//                    if (day == parseDate("dd",parse) && parse.getHours() % 2 == 0){
//                        voList.add(new SmartWaterVO(entity.getTotal(),parseDate("HH",parse) + ""));
//                    }
//                }else if (FlowmeterUnitEnum.getMonthUnit().equals(unit)){
//                    //  最近一月  时间单位是天 - 00:00
//                    if (0 == parseDate("HH",parse)){
//                        voList.add(new SmartWaterVO(entity.getTotal(),parseDate("MMdd",parse) + ""));
//                    }
//                }else if (FlowmeterUnitEnum.getSeasonUnit().equals(unit)){
//                    //  最近三个月，时间单位是周  7天一周期
//                    if (weekDay.equals(DateUtils.getWeekDayString(parse)) && (parseDate("mm",parse) == 0 || iterator.nextIndex() == 1))
//                        voList.add(new SmartWaterVO(entity.getTotal(),parseDate("MMdd",parse) + ""));
//                }else {
//                    //  最近半年，最近一年  时间单位是月
//                    if (0 == parseDate("HH",parse) && 1 == parseDate("dd",parse)){
//                        voList.add(new SmartWaterVO(entity.getTotal(),parseDate("MM",parse) + ""));
//                    }
//                }
//            }
//            return (List<T>) dealList(voList);
//        }
    }

    @Override
    public void saveInfluxData(String gateId, AcrelFlowmeterDetailVO<?> vo, String time) {

        AcrelWaterFlowmeterVO object = JSONObject.parseObject(vo.getValues().toString(), AcrelWaterFlowmeterVO.class);
        Long homeId = equipmentMapper.selectHomeIdByDeviceSn(gateId + ":" + vo.getId());

        String cacheKey = CacheConstants.WATER_LAST_FLOWMETER + homeId;
        String lastMonthWaterTotal = service.getCacheObject(cacheKey);
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(DateUtils.parseDate(time));
        //  当月用水量 = 当前用水总量 - 上个月记录的用水总量
        String waterMeter = new BigDecimal(object.getWaterTatol()).subtract(StringUtils.isEmpty(lastMonthWaterTotal)
                ?
                new BigDecimal("0.0")   // 如果没有上个月数据可以定义为新用户，当前用水量 = 当月用水量
                :
                new BigDecimal(lastMonthWaterTotal)).setScale(2, RoundingMode.HALF_UP).toString();
        //  redis当月用水量
        service.setCacheObject(CacheConstants.WATER_METER + homeId,waterMeter);
        service.setCacheObject(CacheConstants.WATER_METER_NOW + homeId,object.getWaterTatol());
        if (gc.get(Calendar.DAY_OF_MONTH) == gc.getActualMinimum(Calendar.DAY_OF_MONTH)
                && gc.get(Calendar.HOUR) == 0
                    && gc.get(GregorianCalendar.MINUTE) == 0){
            //  当月第一天00:00收到的第一次推送。替换上个月用水量
            service.setCacheObject(cacheKey,object.getWaterTatol());
        }
        //  整点插入 - 时序数据库
        if (gc.get(GregorianCalendar.MINUTE) == 0){
            mapper.save(new WaterFlowmeterEntity(homeId + "",object.getWaterTatol(), Instant.ofEpochMilli(DateUtils.addHours(new Date(),8).getTime())));
        }
        //  设备在线状态
        equipmentMapper.updateEquipmentOnline(gateId + ":" +vo.getId(),Long.parseLong(vo.getStatus()));

        //  计算瞬时流水量
        String flowCacheKey = CacheConstants.WATER_METER +"minute:" + homeId;
        BigDecimal lastMin = new BigDecimal(service.getCacheObject(flowCacheKey) == null ? object.getWaterTatol() : service.getCacheObject(flowCacheKey).toString());
        BigDecimal divide = new BigDecimal(object.getWaterTatol()).subtract(lastMin).divide(new BigDecimal(60),2, RoundingMode.HALF_UP);
        service.setCacheObject(CacheConstants.WATER_METER + "insflow:" + homeId,divide.toString());
        service.setCacheObject(flowCacheKey,object.getWaterTatol());
    }

    @Override
    public boolean queryOnlineInfo(String deviceSn) {
        return ONLINE_STATUS.equals(equipmentMapper.selectOnlineByDeviceSn(deviceSn));
    }

    @Override
    public void saveAlarmData(String gateId, AcrelFlowmeterDetailVO<?> vo) {
        this.saveAlarmInfo(gateId + ":" +vo.getId(),"尊敬的用户，您的家庭“%s”中‘%s’因检测到漏水而导致自动关闭水阀，请及时寻求专业人员进行检修","智能水阀漏水通知");
    }

    /**
     *  按照指定格式解析时间
     * @param format 时间格式
     * @param date 时间date
     * @return
     */
    private int parseDate(String format,Date date){
        return Integer.parseInt(DateUtils.parseDateToStr(format,date));
    }

    @Override
    public EquipmentResponse getEquipmentInfo(EquipmentDO equipmentDO) {

        Long homeId = this.equipmentMapper.selectHomeIdByDeviceSn(equipmentDO.getDeviceSn());
        SmartWaterVO response = null;
        if (switchKey.contains(equipmentDO.getDeviceSn())){

            response = new SmartWaterVO(service.getCacheObject(CacheConstants.WATER_SWITCH_STATUS + homeId),"20.2","","0.12");
        }else {

            response = new SmartWaterVO(service.getCacheObject(CacheConstants.WATER_SWITCH_STATUS + homeId),
                    service.getCacheObject(CacheConstants.WATER_METER + homeId),"",
                    service.getCacheObject(CacheConstants.WATER_METER + "insflow:" + homeId));
        }
        response.setSupplier(equipmentDO.getSupplier());
        response.setCandidateModel(baseService.getCandidateModel(equipmentDO.getDeviceBaseId()));
        response.setDeviceSn(equipmentDO.getDeviceSn());
        return response;
    }

    @Override
    public Boolean control(EquipmentDO equipmentDO, String type, String value) {

        String cacheKey = CacheConstants.WATER_SWITCH_STATUS;
        log.info("Special Virtual DeviceSn : {}",JSONObject.toJSONString(switchKey));
        if (switchKey.contains(equipmentDO.getDeviceSn())){return virtualDevice(equipmentDO.getDeviceSn(),value,cacheKey);}
        Long homeId = this.publishMessage(equipmentDO, Boolean.parseBoolean(value), type);

        //  记录当前状态（操作之前的开关状态）
        service.setCacheObject(cacheKey + ":last:" + homeId,service.getCacheObject(cacheKey + homeId));
        return true;
    }

    private List<SmartWaterVO> AutoDayData(){

        List<SmartWaterVO> list = new ArrayList<>();
        list.add(new SmartWaterVO("0.31",DateUtils.parseDateToStr("HH",new Date())));
        list.add(new SmartWaterVO("0.31",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),2))));
        list.add(new SmartWaterVO("0.92",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),4))));
        list.add(new SmartWaterVO("0.13",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),6))));
        list.add(new SmartWaterVO("0.92",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),8))));
        list.add(new SmartWaterVO("6.74",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),10))));
        list.add(new SmartWaterVO("2.01",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),12))));
        list.add(new SmartWaterVO("0.08",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),14))));
        list.add(new SmartWaterVO("0.92",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),16))));
        list.add(new SmartWaterVO("0.92",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),18))));
        list.add(new SmartWaterVO("0.78",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),20))));
        list.add(new SmartWaterVO("3.12",DateUtils.parseDateToStr("HH",DateUtils.addHours(new Date(),22))));
        return list;
    }
    private List<SmartWaterVO> AutoData() {

        Date lastMonth = DateUtils.addMonths(new Date(), -1);
        List<SmartWaterVO> list = new ArrayList<>();
        list.add(new SmartWaterVO("11.11",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,0))));
        list.add(new SmartWaterVO("10.01",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,5))));
        list.add(new SmartWaterVO("12.22",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,10))));
        list.add(new SmartWaterVO("11.75",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,15))));
        list.add(new SmartWaterVO("18.34",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,20))));
        list.add(new SmartWaterVO("12.12",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,25))));
        list.add(new SmartWaterVO("12.11",DateUtils.parseDateToStr("MM-dd",DateUtils.addDays(lastMonth,30))));
        return list;
    }

    private List<SmartWaterVO> AutoYear() {

        Map<String, String> map = initMonthDataMap();

        List<SmartWaterVO> list = new ArrayList<>();
        Date date = DateUtils.addMonths(new Date(), -12);

        for (int index = 0;index <= 11;index ++){
            String month = DateUtils.parseDateToStr("M月", DateUtils.addMonths(date, index));
            list.add(new SmartWaterVO(map.get(month),month));
        }
        return list;
    }

    private List<SmartWaterVO> AutoHalfYear() {

        Map<String, String> map = initMonthDataMap();
        List<SmartWaterVO> list = new ArrayList<>();
        Date date = DateUtils.addMonths(new Date(), -6);

        for (int index = 0;index <= 5;index ++){
            String month = DateUtils.parseDateToStr("M月", DateUtils.addMonths(date, index));
            list.add(new SmartWaterVO(map.get(month),month));
        }
        return list;
    }

    private List<SmartWaterVO> AutoSeasonData() {

        String cacheKey = "virtual:water:season";
        Object object = service.getCacheObject(cacheKey);
        if (object != null){
            return JSONObject.parseArray(object.toString(), SmartWaterVO.class);
        }
        List<SmartWaterVO> list = new ArrayList<>();
        Date date = DateUtils.addMonths(new Date(), -3);

        for (int index = 0;index <= 3 * 30;index ++){
            if (index % 7 == 0){
                list.add(new SmartWaterVO(BigDecimal.valueOf(Math.random() * 10).setScale(2,BigDecimal.ROUND_CEILING).toString(),DateUtils.parseDateToStr("MM月dd日",DateUtils.addDays(date,index))));
            }
        }
        service.setCacheObject(cacheKey,list,1, TimeUnit.DAYS);
        return list;
    }

    private List<SmartWaterVO> dealList(List<SmartWaterVO> voList) {

        List<SmartWaterVO> list = new ArrayList<>();
        voList.sort(Comparator.comparingInt(x -> Integer.parseInt(x.getDimension())));
        BigDecimal meter = new BigDecimal("0");
        ListIterator<SmartWaterVO> iterator = voList.listIterator();
        String key = "";
        if (iterator.hasNext()){
            SmartWaterVO next = iterator.next();
            meter = new BigDecimal(next.getFlow());
            key = next.getDimension();
        }
        while (iterator.hasNext()){
            SmartWaterVO next = iterator.next();
            BigDecimal tempMeter = new BigDecimal(next.getFlow());
            BigDecimal v = tempMeter.subtract(meter).setScale(2,RoundingMode.HALF_UP);
            list.add(new SmartWaterVO(v.compareTo(new BigDecimal("0.0")) > 0 ? v.toString() : "0.00",key));
            key = next.getDimension();
            meter = tempMeter;
        }
        return list;
    }

    private Map<String,String> initMonthDataMap(){

        String cacheKey = "acrel:water:virtual:data";

        Map<String,String> waterTotalMap = service.getCacheObject(cacheKey);
        if (waterTotalMap == null){

            waterTotalMap = new HashMap<>();
            final BigDecimal min = new BigDecimal("20");
            final BigDecimal max = new BigDecimal("30");
            //  初始化Map
            for (int index = 1;index <= 12;index ++){
                waterTotalMap.put(index + "月", RandomUtil.randomBigDecimal(min, max).setScale(2,BigDecimal.ROUND_CEILING).toString());
            }
            service.setCacheObject(cacheKey,waterTotalMap);
        }
        return waterTotalMap;
    }
}
